#include <iostream>
#include <cstring>
#include <cerrno>

#include "ResourceManager.h"

MY_SHELL_NAMESPACE_BEGIN
// 是否报错
bool if_report_error = false;

// 对当前文件之外的文件不可访问的函数
// 获取文件信息
std::vector<std::string> getFileInformation(const std::string& file_name)
{
    // 用于存储文件信息
    std::vector<std::string> ans = std::vector<std::string>();
    // 用于获取文件信息
    struct stat file_stat;
    // 获取文件信息成功
    if (stat(file_name.c_str(), &file_stat) == 0)
    {
        // 存储文件类型和权限信息
        ans.push_back(std::to_string(file_stat.st_mode & 0777));
        // 根据文件所有者的用户id获取用户名，并将其存储
        struct passwd* pw = getpwuid(file_stat.st_uid);
        if (pw != nullptr)
        {
            ans.push_back(std::string(pw->pw_name));
        }
        else
        {
            ans.push_back(std::string("NULL"));
        }
        // 存储文件大小，单位为byte
        ans.push_back(std::to_string(file_stat.st_size) + " byte");
        // 存储文件最终修改时间
        std::time_t modified_time = file_stat.st_mtime;
        std::string modified_time_str = std::string(std::ctime(&modified_time));
        modified_time_str.pop_back();
        ans.push_back(modified_time_str);
    }
    // 获取文件信息失败，报告错误
    else
    {
        if (mysh::if_report_error) std::cerr << "\033[31m" << "无法获取文件：" << file_name << "的信息，错误信息：" << std::strerror(errno) << std::endl;
    }
    return ans;
}
MY_SHELL_NAMESPACE_END

// 默认构造
mysh::ResourceManager::ResourceManager() :files_trie(nullptr), if_root_path_legal(true)
{
    // 根路径默认为家路径
    this->root_path = std::string(getenv("HOME"));
    this->findFiles();
}

// 根据所给的当前路径字符串构造
mysh::ResourceManager::ResourceManager(const std::string& cur_path, bool if_report_error): files_trie(nullptr)
{
    mysh::if_report_error = if_report_error;
    // 判断所给路径字符串是否存在或是否是一个目录
    if (!std::filesystem::exists(cur_path) || !std::filesystem::is_directory(cur_path))
    {
        if (mysh::if_report_error) std::cerr << "\033[31m" << "路径不存在或不是一个目录: \"" << cur_path << "\"" << "\033[0m" << std::endl;
        this->root_path = std::filesystem::path();
        this->if_root_path_legal = false;
    }
    else
    {
        this->root_path = cur_path;
        this->if_root_path_legal = true;
    }
    // 查找当前路径下的所有文件
    this->findFiles();
}

// 默认析构
mysh::ResourceManager::~ResourceManager()
{
    // 清空根路径
    this->root_path.clear();
    // 清空当前所有文件的数组及字典树
    this->clearCurFiles();
}

// 清空当前路径下的所有查找到的文件名的数组、字典树
void mysh::ResourceManager::clearCurFiles()
{
    // 若当前的根路径不存在或不是一个目录，则表示没有进行构造，直接返回
    if (!this->if_root_path_legal) return;
    // 释放字典树
    delete this->files_trie;
    this->files_trie = nullptr;
    // 清空数组
    this->cur_folders.clear();
    this->cur_regular_files.clear();
    this->cur_others.clear();
}

// 查找当前路径下的所有文件名
void mysh::ResourceManager::findFiles()
{
    // 若当前根路径不存在或不是一个目录，则不进行查找，直接返回
    if (!this->if_root_path_legal) return;
    // 初始化字典树
    this->files_trie = new myds::Trie();
    // 存储当前目录及父目录的路径
    this->cur_others.push_back("./");
    this->cur_others.push_back("../");
    // 逐步查找文件
    for (const auto& entry : std::filesystem::directory_iterator(this->root_path)) 
    {
        // 当前查找到的文件的文件名
        auto file_name = entry.path().filename();
        std::string file_name_str = file_name.string();
        // 文件名为空，查找下一个
        if (file_name_str == "") continue;
        // 文件名不空，将其加入字典树，并判断是否为隐藏文件，若是则加入cur_others数组，否则根据该文件是否为目录或普通文件放入cur_folders或cur_regular_files中
        this->files_trie->insert(file_name_str);
        if (file_name_str[0] != '.')
        {
            if (entry.is_directory()) 
            {
                this->cur_folders.push_back(file_name_str + "/");
            } 
            else if (entry.is_regular_file()) 
            {
                this->cur_regular_files.push_back(file_name_str);
            } 
        }
        else 
        {
            if (entry.is_directory()) file_name_str += "/";
            this->cur_others.push_back(file_name_str);
        }
    }
}

// 根据新路径字符串，查找该路径下的所有文件名，存储到一个字符串数组中并返回
std::vector<std::string> mysh::ResourceManager::createFilesArrayFromRootPath(const std::string& new_path)
{
    // 路径不存在或不是一个目录，返回空数组并报告错误
    if (!std::filesystem::exists(new_path) || !std::filesystem::is_directory(new_path))
    {
        if (mysh::if_report_error) std::cerr << "\033[31m" << "路径不存在或不是一个目录: \"" << new_path << "\"" << "\033[0m" << std::endl;
        return std::vector<std::string>();
    }
    // 路径存在或是一个目录，查找该路径下的所有文件并将文件名存入数组中以返回给函数调用者
    else
    {
        std::vector<std::string> ans = std::vector<std::string>();
        for (const auto& entry : std::filesystem::directory_iterator(std::filesystem::path(new_path)))
        {
            auto file_name = entry.path().filename();
            std::string file_name_str = file_name.string();
            if (file_name_str == "") continue;
            ans.push_back(file_name_str);
        }
        return ans;
    }
}

// 修改当前根路径为new_pah，返回是否修改成功
bool mysh::ResourceManager::changeRootPath(const std::string& new_path)
{
    // 当前路径不存在或不是一个目录，返回false
    if (!std::filesystem::exists(new_path) || !std::filesystem::is_directory(new_path))
    {
        return false;
    }
    // 新路径与原路经不同，修改根路径并重新查找文件
    else if (new_path != this->root_path.string())
    {
        this->root_path = new_path;
        this->if_root_path_legal = true;
        this->clearCurFiles();
        this->findFiles();
    }
    return true;
}

// 根据所给前缀，在字典树中查找所有该前缀的后缀
std::vector<std::string> mysh::ResourceManager::getSuffix(const std::string& prefix)
{
    // 未构造字典树，返回空数组
    if (this->files_trie == nullptr) return std::vector<std::string>();
    // 查找前缀最后一个字符所处节点
    myds::Trie* node = this->files_trie->search(prefix);
    // node为空表示无后缀，返回空数组
    if (node == nullptr) return std::vector<std::string>();
    // 从node开始查找所有后缀并返回
    std::vector<std::string> ans = std::vector<std::string>();
    node->getSuffix(ans);
    return ans;
}

// 打印当前所有文件名
void mysh::ResourceManager::printCurrentAllFiles()
{
    if (!this->if_root_path_legal) return;
    std::cerr << "\033[33m" << "当前路径：\n" << "\033[0m";
    std::cerr << this->root_path.string() << std::endl;
    bool if_all_empty = true;
    if (!this->cur_folders.empty())
    {
        std::cerr << "\033[32m" << "当前路径下的文件夹：\n" << "\033[0m";
        for (const std::string& folder : this->cur_folders)
        {
            std::cerr << folder + "\t";
            if (!isatty(STDOUT_FILENO))
            {
                std::cout << folder << ' ';
            }
        }
        std::cerr << std::endl;
        if_all_empty = false;
    }
    if (!this->cur_regular_files.empty())
    {
        std::cerr << "\033[34m" << "当前路径下的普通文件：\n" << "\033[0m";
        for (const std::string& file : this->cur_regular_files)
        {
            std::cerr << file + "\t";
            if (!isatty(STDOUT_FILENO))
            {
                std::cout << file << ' ';
            }
        }
        std::cerr << std::endl;
        if_all_empty = false;
    }
    if (!this->cur_others.empty())
    {
        std::cerr << "\033[35m" << "当前路径下的其他文件：\n" << "\033[0m";
        for (const std::string& other : this->cur_others)
        {
            std::cerr << other + "\t";
            if (!isatty(STDOUT_FILENO))
            {
                std::cout << other << ' ';
            }
        }
        std::cerr << std::endl;
        if_all_empty = false;
    }
    if (!isatty(STDOUT_FILENO))
    {
        std::cout << std::flush;
    }
    if (if_all_empty)
    {
        if (mysh::if_report_error) std::cerr << "\033[31m" << "当前路径下无任何文件" << "\033[0m" << std::endl;
    }
}

// 以长列表形式打印当前所有文件名并显示所有文件的信息
void mysh::ResourceManager::printCurrentFilesInList()
{
    if (!this->if_root_path_legal) return;
    bool if_all_empty = true;
    std::cerr << "\033[33m" << "当前路径：\n" << "\033[0m";
    std::cerr << this->root_path.string() << std::endl;
    if (!this->cur_folders.empty())
    {
        if_all_empty = false;
        for (const std::string& str : this->cur_folders)
        {
            std::string folder = this->root_path.string() + "/" + str;
            std::vector<std::string> info = mysh::getFileInformation(folder);
            if (!info.empty())
            {
                std::cerr << "\033[32m" << str << "\033[0m" << std::endl;
                std::cerr << "  " << "\033[34m" << "文件权限：" << "\033[0m";
                std::cerr << info[0] << std::endl;
                std::cerr << "  " << "\033[34m" << "文件拥有者：" << "\033[0m";
                std::cerr << info[1] << std::endl;
                std::cerr << "  " << "\033[34m" << "文件大小：" << "\033[0m";
                std::cerr << info[2] << std::endl;
                std::cerr << "  " << "\033[34m" << "文件修改时间：" << "\033[0m";
                std::cerr << info[3] << std::endl;
                if (!isatty(STDOUT_FILENO))
                {
                    std::cout << str << ',' << info[0] << ',' << info[1] << ',' << info[2] << ',' << info[3] << ' ';
                }
            }
        }
    }
    if (!this->cur_regular_files.empty())
    {
        if_all_empty = false;
        for (const std::string& str : this->cur_regular_files)
        {
            std::string file = this->root_path.string() + "/" + str;
            std::vector<std::string> info = mysh::getFileInformation(file);
            if (!info.empty())
            {
                std::cerr << "\033[32m" << str << "\033[0m" << std::endl;
                std::cerr << "  " << "\033[34m" << "文件权限：" << "\033[0m";
                std::cerr << info[0] << std::endl;
                std::cerr << "  " << "\033[34m" << "文件拥有者：" << "\033[0m";
                std::cerr << info[1] << std::endl;
                std::cerr << "  " << "\033[34m" << "文件大小：" << "\033[0m";
                std::cerr << info[2] << std::endl;
                std::cerr << "  " << "\033[34m" << "文件修改时间：" << "\033[0m";
                std::cerr << info[3] << std::endl;
                if (!isatty(STDOUT_FILENO))
                {
                    std::cout << str << ',' << info[0] << ',' << info[1] << ',' << info[2] << ',' << info[3] << ' ';
                }
            }
        }
    }
    if (!this->cur_others.empty())
    {
        if_all_empty = false;
        for (const std::string& str : this->cur_others)
        {
            if (str == "./" || str == "../")continue;
            std::string other = this->root_path.string() + "/" + str;
            std::vector<std::string> info = mysh::getFileInformation(other);
            if (!info.empty())
            {
                std::cerr << "\033[32m" << str << "\033[0m" << std::endl;
                std::cerr << "  " << "\033[34m" << "文件权限：" << "\033[0m";
                std::cerr << info[0] << std::endl;
                std::cerr << "  " << "\033[34m" << "文件拥有者：" << "\033[0m";
                std::cerr << info[1] << std::endl;
                std::cerr << "  " << "\033[34m" << "文件大小：" << "\033[0m";
                std::cerr << info[2] << std::endl;
                std::cerr << "  " << "\033[34m" << "文件修改时间：" << "\033[0m";
                std::cerr << info[3] << std::endl;
                if (!isatty(STDOUT_FILENO))
                {
                    std::cout << str << ',' << info[0] << ',' << info[1] << ',' << info[2] << ',' << info[3] << ' ';
                }
            }
        }
    }
    if (!isatty(STDOUT_FILENO))
    {
        std::cout << std::flush;
    }
    if (if_all_empty)
    {
        if (mysh::if_report_error) std::cerr << "\033[31m" << "当前路径下无任何文件" << "\033[0m" << std::endl;
    }
}